import copy
import logging
from collections import deque
from enum import Enum
from typing import (
    Dict,
    Text,
    Any,
    Optional,
    Iterator,
    Generator,
    Type,
    List,
    Deque,
    Iterable,
)

from rasa.core import events                                # pytype: disable=pyi-error
from rasa.core.actions.action import ACTION_LISTEN_NAME     # pytype: disable=pyi-error
from rasa.core.conversation import Dialogue                 # pytype: disable=pyi-error
from rasa.core.events import (                              # pytype: disable=pyi-error
    UserUttered,
    ActionExecuted,
    Event,
    SlotSet,
    Restarted,
    ActionReverted,
    UserUtteranceReverted,
    BotUttered,
    Form,
    SessionStarted,
)
from rasa.core.domain import Domain  # pytype: disable=pyi-error
from rasa.core.slots import Slot

logger = logging.getLogger(__name__)


class EventVerbosity(Enum):
    """筛选要包含在跟踪器转储中的事件。"""

    # 不包含任何事件
    NONE = 1

    # 包括所有有助于跟踪器状态的事件，这些都是重构跟踪器状态所需的全部
    APPLIED = 2

    # 包括更多事件，在这种情况下，所有事件都来自最近的重启事件。 这还包括恢复的话语和撤消的动作。
    AFTER_RESTART = 3

    # 包括每个记录的事件
    ALL = 4


class AnySlotDict(dict):
    """一个插槽字典，通过按需创建插槽来假装每个插槽都存在。

    这只使用通用插槽类型！这意味着某些功能不起作用，例如适当地增加插槽的特征。"""

    def __missing__(self, key) -> Slot:
        value = self[key] = Slot(key)
        return value

    def __contains__(self, key) -> bool:
        return True


class DialogueStateTracker:
    """保持对话的状态的类。

    字段max_event_history将只提供这些最后的事件，
    它可以在tracker_store中设置"""

    @classmethod
    def from_dict(
        cls,
        sender_id: Text,
        events_as_dict: List[Dict[Text, Any]],
        slots: Optional[List[Slot]] = None,
        max_event_history: Optional[int] = None,
    ) -> "DialogueStateTracker":
        """从转储创建跟踪器。

            转储应该是转储事件的数组。恢复跟踪器时，将重放这些事件以重新创建状态"""

        evts = events.deserialise_events(events_as_dict)
        return cls.from_events(sender_id, evts, slots, max_event_history)

    @classmethod
    def from_events(
        cls,
        sender_id: Text,
        evts: List[Event],
        slots: Optional[List[Slot]] = None,
        max_event_history: Optional[int] = None,
    ):
        tracker = cls(sender_id, slots, max_event_history)
        for e in evts:
            tracker.update(e)
        return tracker

    def __init__(
        self,
        sender_id: Text,
        slots: Optional[Iterable[Slot]],
        max_event_history: Optional[int] = None,
    ) -> None:
        """初始化跟踪器。
        一组事件可以存储在外部，我们将运行所有事件以获得当前状态。
        跟踪器将代表我们在处理对话信息时捕捉到的所有信息。"""

        self._max_event_history = max_event_history     # 要存储的最大事件数
        self.events = self._create_events([])           # 以前看到的事件列表（双端队列）
        self.sender_id = sender_id      # 消息源的id
        if slots is not None:           # 可在此域中填充的插槽
            self.slots = {slot.name: copy.deepcopy(slot) for slot in slots}
        else:
            self.slots = AnySlotDict()

        # 跟踪器的当前状态-必须通过处理所有事件来重新创建。 这仅定义属性，值在`reset（）`中设置
        self._paused = False        # 如果跟踪器已暂停，则不应采取任何措施
        self.followup_action = ACTION_LISTEN_NAME  # 下一步要执行的确定的预定动作
        self.latest_action_name = None
        self.latest_message = None       # 存储用户最近发送的消息
        self.latest_bot_utterance = None
        self._reset()  # 重置
        self.active_form = {}

    ###
    # 公共跟踪器接口
    ###
    def current_state(
        self, event_verbosity: EventVerbosity = EventVerbosity.NONE
    ) -> Dict[Text, Any]:
        """以对象的形式返回当前跟踪器状态。"""

        if event_verbosity == EventVerbosity.ALL:
            evts = [e.as_dict() for e in self.events]
        elif event_verbosity == EventVerbosity.AFTER_RESTART:
            evts = [e.as_dict() for e in self.events_after_latest_restart()]
        elif event_verbosity == EventVerbosity.APPLIED:
            evts = [e.as_dict() for e in self.applied_events()]
        else:
            evts = None

        latest_event_time = None
        if len(self.events) > 0:
            latest_event_time = self.events[-1].timestamp

        return {
            "sender_id": self.sender_id,
            "slots": self.current_slot_values(),
            "latest_message": self.latest_message.parse_data,
            "latest_event_time": latest_event_time,
            "followup_action": self.followup_action,
            "paused": self.is_paused(),
            "events": evts,
            "latest_input_channel": self.get_latest_input_channel(),
            "active_form": self.active_form,
            "latest_action_name": self.latest_action_name,
        }

    def past_states(self, domain) -> deque:
        """根据历史记录生成此跟踪器的过去状态。"""

        generated_states = domain.states_for_tracker_history(self)
        return deque(frozenset(s.items()) for s in generated_states)

    def change_form_to(self, form_name: Text) -> None:
        """激活或停用表单"""
        if form_name is not None:
            self.active_form = {
                "name": form_name,
                "validate": True,
                "rejected": False,
                "trigger_message": self.latest_message.parse_data,
            }
        else:
            self.active_form = {}

    def set_form_validation(self, validate: bool) -> None:
        """切换到表单验证"""
        self.active_form["validate"] = validate

    def reject_action(self, action_name: Text) -> None:
        """通知活动的表单已被拒绝"""
        if action_name == self.active_form.get("name"):
            self.active_form["rejected"] = True

    def set_latest_action_name(self, action_name: Text) -> None:
        """设定最新动作名称
        并重置表单验证和拒绝参数
        """
        self.latest_action_name = action_name
        if self.active_form.get("name"):
            # 如果某个表单处于活动状态，则重置表单验证
            self.active_form["validate"] = True
        if action_name == self.active_form.get("name"):
            # 如果再次预测，则重置表单拒绝
            self.active_form["rejected"] = False

    def current_slot_values(self) -> Dict[Text, Any]:
        """返回插槽的当前设置值"""
        return {key: slot.value for key, slot in self.slots.items()}

    def get_slot(self, key: Text) -> Optional[Any]:
        """检索插槽的值。"""

        if key in self.slots:
            return self.slots[key].value
        else:
            logger.info(f"Tried to access non existent slot '{key}'")
            return None

    def get_latest_entity_values(self, entity_type: Text) -> Iterator[Text]:
        """获取在最新消息中为传递的实体名称找到的实体值。

        如果只对给定类型的第一个实体感兴趣，
        请使用“ next（tracker.get_latest_entity_values（“my_entity_name”），None）”。
        如果未找到任何实体，则默认为“None”。
        """

        return (
            x.get("value")
            for x in self.latest_message.entities
            if x.get("entity") == entity_type
        )

    def get_latest_input_channel(self) -> Optional[Text]:
        """获取最新的UserUttered事件的input_channel的名称"""

        for e in reversed(self.events):
            if isinstance(e, UserUttered):
                return e.input_channel
        return None

    def is_paused(self) -> bool:
        """说明跟踪器当前是否暂停。"""
        return self._paused

    def idx_after_latest_restart(self) -> int:
        """返回事件列表中最近一次重新启动的idx。

        如果会话尚未重新启动，则返回“0”。
        """

        for i, event in enumerate(reversed(self.events)):
            if isinstance(event, Restarted):
                return len(self.events) - i

        return 0

    def events_after_latest_restart(self) -> List[Event]:
        """返回最近一次重新启动后的事件列表。"""
        return list(self.events)[self.idx_after_latest_restart():]

    def init_copy(self) -> "DialogueStateTracker":
        """创建具有相同初始值的新状态跟踪器。"""
        from rasa.core.channels.channel import UserMessage

        return DialogueStateTracker(
            UserMessage.DEFAULT_SENDER_ID, self.slots.values(), self._max_event_history
        )

    def generate_all_prior_trackers(
        self,
    ) -> Generator["DialogueStateTracker", None, None]:
        """返回一个此跟踪器以前的跟踪器的生成器。

        结果数组表示每个操作之前的跟踪器。"""

        tracker = self.init_copy()

        ignored_trackers = []
        latest_message = tracker.latest_message

        for i, event in enumerate(self.applied_events()):
            if isinstance(event, UserUttered):
                if tracker.active_form.get("name") is None:
                    # 在表单之前存储最新的用户消息
                    latest_message = event

            elif isinstance(event, Form):
                # 表单被激活或停用，
                # 因此覆盖跟踪器的最新消息
                tracker.latest_message = latest_message

            elif isinstance(event, ActionExecuted):
                # 产生中间状态
                if tracker.active_form.get("name") is None:
                    # no form is active, just yield as is
                    yield tracker

                elif tracker.active_form.get("rejected"):
                    yield from ignored_trackers
                    ignored_trackers = []

                    if not tracker.active_form.get(
                        "validate"
                    ) or event.action_name != tracker.active_form.get("name"):
                        # persist latest user message
                        # that was rejected by the form
                        latest_message = tracker.latest_message
                    else:
                        # form was called with validation, so
                        # override tracker's latest message
                        tracker.latest_message = latest_message

                    yield tracker

                elif event.action_name != tracker.active_form.get("name"):
                    # 不知道表单是否会成功执行，
                    # 所以存储这个跟踪器以备后用
                    tr = tracker.copy()
                    # 表单是通过验证调用的，
                    # 因此覆盖跟踪器的最新消息
                    tr.latest_message = latest_message
                    ignored_trackers.append(tr)

                if event.action_name == tracker.active_form.get("name"):
                    # 表单已成功执行，
                    # 因此删除所有存储的跟踪器
                    ignored_trackers = []

            tracker.update(event)

        # yields the final state
        if tracker.active_form.get("name") is None:
            # no form is active, just yield as is
            yield tracker
        elif (
            tracker.active_form.get("rejected")
            or tracker.latest_action_name == ACTION_LISTEN_NAME
        ):
            # either a form was rejected or user uttered smth yield trackers
            yield from ignored_trackers
            yield tracker

    def applied_events(self) -> List[Event]:
        """返回应该应用的所有操作-不包含已恢复的事件。"""

        def undo_till_previous(event_type, done_events):
            """从 `done_events` 中删除事件，直到找到第一次出现的 `event_type`，该事件也被删除."""

            # 列表被修改 - 因此我们需要复制事件！
            for e in reversed(done_events[:]):
                del done_events[-1]
                if isinstance(e, event_type):
                    break

        applied_events = []
        for event in self.events:
            if isinstance(event, (Restarted, SessionStarted)):
                applied_events = []
            elif isinstance(event, ActionReverted):
                undo_till_previous(ActionExecuted, applied_events)
            elif isinstance(event, UserUtteranceReverted):
                # 看到一个用户发出的事件自动意味着在它之前有一个监听事件，
                # 所以我们将首先倒回用户话语，
                # 然后在它之前获得动作
                # （也删除它之前的 `action_listen` 动作）。
                undo_till_previous(UserUttered, applied_events)
                undo_till_previous(ActionExecuted, applied_events)
            else:
                applied_events.append(event)

        return applied_events

    def replay_events(self) -> None:
        """根据事件列表更新跟踪器。"""

        applied_events = self.applied_events()
        for event in applied_events:
            event.apply_to(self)

    def recreate_from_dialogue(self, dialogue: Dialogue) -> None:
        """使用序列化的“对话框”来更新跟踪器状态。

        这使用的状态保持在TrackerStore中。
        如果在调用此方法之前跟踪器为空，则最终状态将与创建对话的跟踪器相同。"""

        if not isinstance(dialogue, Dialogue):
            raise ValueError(
                "story {} is not of type Dialogue. "
                "Have you deserialized it?".format(dialogue)
            )

        self._reset()  # 重置
        self.events.extend(dialogue.events)
        self.replay_events()

    def copy(self) -> "DialogueStateTracker":
        """创建此跟踪器的副本"""
        return self.travel_back_in_time(float("inf"))

    def travel_back_in_time(self, target_time: float) -> "DialogueStateTracker":
        """创建具有特定时间戳的状态的新跟踪器。

        将创建一个新的跟踪器，并将重放先前通过的时间戳中的所有事件。
        将包括恰好在目标时间发生的事件。"""

        tracker = self.init_copy()

        for event in self.events:
            if event.timestamp <= target_time:
                tracker.update(event)
            else:
                break

        return tracker  # yields the final state

    def as_dialogue(self) -> Dialogue:
        """返回包含所有回合的“ Dialogue”对象。

        可以对其进行序列化，以后再用于准确恢复此跟踪器的状态。."""

        return Dialogue(self.sender_id, list(self.events))

    def update(self, event: Event, domain: Optional[Domain] = None) -> None:
        """根据"Event"修改跟踪器的状态"""
        if not isinstance(event, Event):  # pragma: no cover
            raise ValueError("event to log must be an instance of a subclass of Event.")
        # 添加并修改跟踪器状态
        self.events.append(event)
        event.apply_to(self)

        if domain and isinstance(event, UserUttered):
            # 将所有实体存储为槽
            for e in domain.slots_for_entities(event.parse_data["entities"]):
                self.update(e)

    def export_stories(self, e2e: bool = False) -> Text:
        """将跟踪器作为Rasa core故事格式的故事转储。

        以字符串形式返回转储的跟踪器。"""
        from rasa.core.training.structures import Story

        story = Story.from_events(self.applied_events(), self.sender_id)
        return story.as_story_string(flat=True, e2e=e2e)

    def export_stories_to_file(self, export_path: Text = "debug.md") -> None:
        """将跟踪器作为故事转储到文件中。"""
        import rasa.utils.io

        rasa.utils.io.write_text_file(
            self.export_stories() + "\n", export_path, append=True
        )

    def get_last_event_for(
        self,
        event_type: Type[Event],
        action_names_to_exclude: List[Text] = None,
        skip: int = 0,
    ) -> Optional[Event]:
        """获取实际应用的给定类型的最后一个事件。

         参数：
             event_type： 您要查找的事件的类型。
             skip：       在返回事件之前跳过n个可能的结果。
             action_names_to_exclude：  “ActionExecuted” 类型的事件，应从结果中排除。
                                        可用于跳过“ action_listen”事件。

         返回值：
             匹配查询的事件；如果没有匹配的事件，则为“None”。
        """

        to_exclude = action_names_to_exclude or []

        def filter_function(e: Event):
            has_instance = isinstance(e, event_type)
            excluded = isinstance(e, ActionExecuted) and e.action_name in to_exclude
            return has_instance and not excluded

        filtered = filter(filter_function, reversed(self.applied_events()))

        for i in range(skip):
            next(filtered, None)

        return next(filtered, None)

    def last_executed_action_has(self, name: Text, skip=0) -> bool:
        """返回上一个“ ActionExecuted”事件是否具有特定名称。

        Args:
            name: 应该匹配的事件的名称。
            skip: 跳过之间的可能结果。

        Returns:
             如果上一次执行的动作的名称为name，则为True，否则为false。
        """

        last: Optional[ActionExecuted] = self.get_last_event_for(
            ActionExecuted, action_names_to_exclude=[ACTION_LISTEN_NAME], skip=skip
        )
        return last is not None and last.action_name == name

    ###
    # 修改跟踪器状态的内部方法。 仅应通过事件调用，而不能直接调用。
    # 而是使用在其apply_to方法中修改跟踪器的事件来更新跟踪器。
    ###
    def _reset(self) -> None:
        """将跟踪器重置为初始状态-不会删除事件！"""

        self._reset_slots()
        self._paused = False
        self.latest_action_name = None
        self.latest_message = UserUttered.empty()
        self.latest_bot_utterance = BotUttered.empty()
        self.followup_action = ACTION_LISTEN_NAME
        self.active_form = {}

    def _reset_slots(self) -> None:
        """将所有插槽设置为其初始值。"""

        for slot in self.slots.values():
            slot.reset()

    def _set_slot(self, key: Text, value: Any) -> None:
        """如果该插槽存在，请设置该插槽的值。"""

        if key in self.slots:
            self.slots[key].value = value
        else:
            logger.error(
                "Tried to set non existent slot '{}'. Make sure you "
                "added all your slots to your domain file."
                "".format(key)
            )

    def _create_events(self, evts: List[Event]) -> Deque[Event]:
        """创建事件列表"""
        if evts and not isinstance(evts[0], Event):  # pragma: no cover
            raise ValueError("events, if given, must be a list of events")
        return deque(evts, self._max_event_history)  # 双端队列

    def __eq__(self, other) -> bool:
        if isinstance(self, type(other)):
            return other.events == self.events and self.sender_id == other.sender_id
        else:
            return False

    def __ne__(self, other) -> bool:
        return not self.__eq__(other)

    def trigger_followup_action(self, action: Text) -> None:
        """执行当前动作后触发另一个动作。"""

        self.followup_action = action

    def clear_followup_action(self) -> None:
        """执行后清除后续操作。"""
        self.followup_action = None

    def _merge_slots(
        self, entities: Optional[List[Dict[Text, Any]]] = None
    ) -> List[SlotSet]:
        """获取实体列表并创建跟踪器插槽设置事件。

        如果实体类型与插槽名称匹配，则通过创建``SlotSet``事件将实体值设置为插槽值。
        """

        entities = entities if entities else self.latest_message.entities
        new_slots = [
            SlotSet(e["entity"], e["value"])
            for e in entities
            if e["entity"] in self.slots.keys()
        ]
        return new_slots
